/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// TGUI - Texus' Graphical User Interface
// Copyright (C) 2012-2025 Bruno Van de Velde (vdv_b@tgui.eu)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef TGUI_TEXT_HPP
#define TGUI_TEXT_HPP

#include <TGUI/Font.hpp>
#include <TGUI/Color.hpp>
#include <TGUI/Vector2.hpp>
#include <TGUI/TextStyle.hpp>
#include <TGUI/RenderStates.hpp>

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace tgui
{
    class BackendText;

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#if TGUI_COMPILED_WITH_CPP_VER >= 20
    // Inline is needed here if we want to build a c++20 module
    inline constexpr unsigned int AutoTextSize = 0xFFFFFFFF;
#else
    constexpr unsigned int AutoTextSize = 0xFFFFFFFF;
#endif

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// @brief Backend-independent wrapper around the backend-specific text class
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    class TGUI_API Text
    {
    public:

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Describes a text piece, before turning it into an actual Text object
        ///
        /// This is used to word-wrap multiple text pieces (e.g. in RichTextLabel) without having to create Text objects for
        /// each piece until after the word-wrap is completed.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        struct Blueprint
        {
            unsigned int characterSize = 0;
            unsigned int style = 0;
            Color        color;
            String       link;
            String       text;
            Vector2u     gapSize;
        };

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns a small distance that text should be placed from the side of a widget as padding.
        ///
        /// This distance is slightly smaller than getExtraHorizontalOffset.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static float getExtraHorizontalPadding(const Font& font, unsigned int characterSize);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns an extra distance that text should be placed from the side of a widget as padding.
        ///
        /// This distance is slightly larger than getExtraHorizontalPadding.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static float getExtraHorizontalOffset(const Font& font, unsigned int characterSize);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the distance that text should be placed from the bottom of the widget as padding.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static float getExtraVerticalPadding(unsigned int characterSize);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the height of a single line of text
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static float getLineHeight(const Font& font, unsigned int characterSize);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the width of a single line of text
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static float getLineWidth(const String &text, const Font& font, unsigned int characterSize, TextStyles textStyle = {});

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Finds the best character size for the text
        ///
        /// @param font    Font of the text
        /// @param height  Height that the text should fill
        /// @param fit     0 to choose best fit, 1 to select font of at least that height, -1 to select font of maximum that height
        ///
        /// @return Chosen character size
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static unsigned int findBestTextSize(const Font& font, float height, int fit = 0);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Takes a string and inserts newlines into it so that the width does not exceed maxWidth.
        ///
        /// @param maxWidth         Maximum width of the text
        /// @param text             The text to wrap
        /// @param font             Font of the text
        /// @param textSize         The text size
        /// @param bold             Should the text be bold?
        ///
        /// @return Text with additional '\n' characters
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static String wordWrap(float maxWidth, const String& text, const Font& font, unsigned int textSize, bool bold);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Takes multiple lines of potentially multiple text pieces, and splits lines so that the width does not exceed maxWidth
        ///
        /// @param maxWidth         Maximum width of the text
        /// @param lines            Existing lines that need to be split if they are too long
        /// @param font             Font of the text
        ///
        /// @return Lines of text pieces (either the same as the input or more lines when some were split)
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static std::vector<std::vector<Blueprint>> wordWrap(float maxWidth, const std::vector<std::vector<Blueprint>>& lines, const Font& font);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public:

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Default constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Text();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Copy constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Text(const Text&);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Move constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Text(Text&&) noexcept = default;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Overload of copy assignment operator
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Text& operator=(const Text&);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Move assignment operator
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        Text& operator=(Text&&) noexcept = default;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Sets the position of the text
        ///
        /// @param position  Position of the text
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setPosition(Vector2f position);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the position of the text
        ///
        /// @return Text position
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Vector2f getPosition() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the size of the text
        ///
        /// @return Size required for drawing the text
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Vector2f getSize() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the text
        ///
        /// @param string  The new text
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setString(const String& string);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the text
        ///
        /// @return The current text
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD const String& getString() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the character size of the text
        ///
        /// @param size  The new text size
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setCharacterSize(unsigned int size);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the character size of the text
        ///
        /// @return The current text size
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getCharacterSize() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the text fill color
        ///
        /// @param color  The new text color
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setColor(Color color);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the text fill color
        ///
        /// @return text color
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Color getColor() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the opacity of the text
        ///
        /// @param opacity  The text opacity
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setOpacity(float opacity);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the opacity of the text
        ///
        /// @return text opacity
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float getOpacity() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the font used for the text
        ///
        /// @param font  The new font
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setFont(const Font& font);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the font of the text
        ///
        /// @return text font
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Font getFont() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the style of the text
        ///
        /// The possible styles can be found in the tgui::TextStyle::Style enum.
        /// You can also pass a combination of multiple styles, for example tgui::TextStyle::Bold | tgui::TextStyle::Italic.
        /// The default style is tgui::TextStyle::Regular.
        ///
        /// @param style  New text style
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setStyle(TextStyles style);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the style of the text
        ///
        /// @return The current text style
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD TextStyles getStyle() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the text outline color
        ///
        /// @param color  The new text outline color
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setOutlineColor(Color color);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the text outline color
        ///
        /// @return text outline color
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Color getOutlineColor() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the text outline thickness
        ///
        /// @param thickness  The new text outline thickness
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setOutlineThickness(float thickness);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the text outline thickness
        ///
        /// @return text outline thickness
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float getOutlineThickness() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Return the position of the @a index-th character
        ///
        /// If @a index is out of range, the position of the end of the string is returned.
        ///
        /// @param index Index of the character
        ///
        /// @return Position of the character
        ///
        /// @warning Unlike the function in sf::Text, this function does not take global transformations into account.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Vector2f findCharacterPos(std::size_t index) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns a small distance that text should be placed from the side of a widget as padding.
        ///
        /// This distance is slightly smaller than getExtraHorizontalOffset.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float getExtraHorizontalPadding() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns an extra distance that text should be placed from the side of a widget as padding.
        ///
        /// This distance is slightly larger than getExtraHorizontalPadding.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float getExtraHorizontalOffset() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the height of a single line of text
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float getLineHeight() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the width of a single line of text
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float getLineWidth() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the internal text
        /// @return Backend text that is used internally
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::shared_ptr<BackendText> getBackendText() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    private:

        std::shared_ptr<BackendText> m_backendText;
        Vector2f     m_position;
        Font         m_font;
        Color        m_color;
        Color        m_outlineColor;
        float        m_opacity = 1;
    };

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#endif // TGUI_TEXT_HPP
